#pragma once
#ifndef __LIT__MOVABLE__TEXT__OVERLAY
#define __LIT__MOVABLE__TEXT__OVERLAY

#include <Ogre.h>
#include <OgreFont.h>
#include <OgreFontManager.h>
#include <LitPrerequisites.h>

namespace Lit
{
 
	class LIT_API MovableTextOverlayAttributes
	{
	public:
		MovableTextOverlayAttributes(const Ogre::String & name, const Ogre::Camera *cam,
							 const Ogre::String & fontName = "BlueHighway", int charHeight = 16, const Ogre::ColourValue & color = Ogre::ColourValue::White, const Ogre::String & getMaterialName = "");
		~MovableTextOverlayAttributes();
 
		void setFontName(const Ogre::String & fontName);
		void setMaterialName(const Ogre::String & getMaterialName);
		void setColor(const Ogre::ColourValue & color);
		void setCharacterHeight(unsigned int height);
 
		const Ogre::String& getName() const {return mName;}
		const Ogre::Camera* getCamera() const {return mpCam;}
		const Ogre::Font* getFont() const {return mpFont;}
		const Ogre::String& getFontName() const {return mFontName;}
		const Ogre::String& getMaterialName() const {return mMaterialName;}
		const Ogre::ColourValue& getColor() const {return mColor;}
		const Ogre::Real getCharacterHeight() const {return mCharHeight;}
 
		const Ogre::String mName;
		const Ogre::Camera *mpCam;
 
		Ogre::Font* mpFont;
		Ogre::String mFontName;
		Ogre::String mMaterialName;
		Ogre::ColourValue mColor;
		Ogre::Real mCharHeight;
	};
 
	class LIT_API MovableTextOverlay {
	public:
		MovableTextOverlay(const Ogre::String & name, const Ogre::String & caption,
							const Ogre::MovableObject *mov, MovableTextOverlayAttributes *attrs);
 
		virtual ~MovableTextOverlay();
 
		void setCaption(const Ogre::String & caption);
		void setUpdateFrequency(Ogre::Real updateFrequency) {mUpdateFrequency = updateFrequency;}
		void setAttributes(MovableTextOverlayAttributes *attrs)
		{
			mAttrs = attrs;
			_updateOverlayAttrs();
		}
 
		const Ogre::String&	getName() const {return mName;}
		const Ogre::String&	getCaption() const {return mCaption;}
		const Ogre::Real getUpdateFrequency() const {return mUpdateFrequency;}
		const bool isOnScreen() const {return mOnScreen;}
		const bool isEnabled() const {return mEnabled;}
		const MovableTextOverlayAttributes* getAttributes() const {return mAttrs;}
 
		void enable(bool enable);
		void update(Ogre::Real timeSincelastFrame);
 
		// Needed for RectLayoutManager.
		int getPixelsTop() {return Ogre::OverlayManager::getSingleton().getViewportHeight() * (mpOvContainer->getTop());}
		int getPixelsBottom() {return Ogre::OverlayManager::getSingleton().getViewportHeight() * (mpOvContainer->getTop() + mpOvContainer->getHeight());}
		int getPixelsLeft() {return Ogre::OverlayManager::getSingleton().getViewportWidth() * mpOvContainer->getLeft();}
		int getPixelsRight() {return Ogre::OverlayManager::getSingleton().getViewportWidth() * (mpOvContainer->getLeft() + mpOvContainer->getWidth());}
 
		void setPixelsTop(int px) {mpOvContainer->setTop((Ogre::Real)px / Ogre::OverlayManager::getSingleton().getViewportHeight());}
		// end
 
	protected:
		void _computeTextWidth();
		void _updateOverlayAttrs();
		void _getMinMaxEdgesOfTopAABBIn2D(Ogre::Real& MinX, Ogre::Real& MinY, Ogre::Real& MaxX, Ogre::Real& MaxY);
		void _getScreenCoordinates(const Ogre::Vector3& position, Ogre::Real& x, Ogre::Real& y);
 
		const Ogre::String mName;
		const Ogre::MovableObject* mpMov;
 
		Ogre::Overlay* mpOv;
		Ogre::OverlayContainer* mpOvContainer;
		Ogre::OverlayElement* mpOvText;
 
		// true if mpOvContainer is visible, false otherwise
		bool mEnabled;
 
		// true if mTextWidth needs to be recalculated
		bool mNeedUpdate;
 
		// Text width in pixels
		Ogre::Real mTextWidth;
 
		// the Text
		Ogre::String mCaption;
 
		// true if the upper vertices projections of the -MovableObject are on screen
		bool mOnScreen;
 
		// the update frequency in seconds
		// mpOvContainer coordinates get updated each mUpdateFrequency seconds.
		Ogre::Real mUpdateFrequency;
 
		// the Font/Material/Color text attributes
		MovableTextOverlayAttributes *mAttrs;
	};
	// It moves the rectangles on the Y axis so they won't overlap.
	class LIT_API RectLayoutManager
	{
	public:
 
		enum MethodType 
		{
			PLACE_ABOVE = 0,	// the overlapping rectangles are placed above the non-overlapping ones
			PLACE_BELOW			// the overlapping rectangles are placed below the non-overlapping ones
		};
 
		class Rect
		{
		public:
			Rect(short left, short top, short right, short bottom)
				: mLeft(left)
				, mTop(top)
				, mRight(right)
				, mBottom(bottom)
				, dy(0)
			{
				if (mBottom <= mTop)
					throw std::exception("Condition Failure (top < bottom) in RectLayoutManager::Rect::Rect");
 
				if (mRight <= mLeft)
					throw std::exception("Condition Failure (left < right) in RectLayoutManager::Rect::Rect");
			}
 
			inline const short getTop() const {return mTop + dy;}
			inline const short getBottom() const {return mBottom + dy;}
			inline const short getLeft() const {return mLeft;}
			inline const short getRight() const {return mRight;}
 
			// STL needs this
			inline bool operator <(const RectLayoutManager::Rect &R) const {return getBottom() < R.getBottom();}
 
			// displacement on Y axis
			short dy;
 
			// original rectangle coordinates
			short mBottom;
			short mTop;
			short mLeft;
			short mRight;
		};
 
		typedef std::list<RectLayoutManager::Rect> RectList;
 
		RectLayoutManager(	unsigned short leftBound,
							unsigned short topBound,
							unsigned short rightBound,
							unsigned short bottomBound,
							MethodType method = PLACE_ABOVE)
		: mMethod(method)
		, mBoundTop(topBound)
		, mBoundLeft(leftBound)
		, mBoundBottom(bottomBound)
		, mBoundRight(rightBound)
		, mMinDistance(1)
		, mMaxRectHeight(0)
		, mDepth(0)
		{
			if (mBoundBottom <= mBoundTop)
				throw std::exception("Condition Failure (mBoundTop < mBoundBottom) in RectLayoutManager::RectLayoutManager");
 
			if (mBoundRight <= mBoundLeft)
				throw std::exception("Condition Failure (mBoundLeft < mBoundRight) in RectLayoutManager::RectLayoutManager");
		}
 
		~RectLayoutManager(){clear();}
 
		const unsigned short getMinDistance() const {return mMinDistance;}
		const unsigned short getMaxRectHeight() const {return mMaxRectHeight;}
		const unsigned short getDepth() const {return mDepth;}
		void getBounds(	unsigned short &left,
						unsigned short &top,
						unsigned short &right,
						unsigned short &bottom)
		{
			left = mBoundLeft;
			top = mBoundTop;
			right = mBoundRight;
			bottom = mBoundBottom;
		}
 
		void setMinDistance(unsigned short minDistance){mMinDistance = minDistance;}
		void setDepth(unsigned short depth){mDepth = depth;}
 
		bool isOutOfBounds(RectLayoutManager::Rect &r)
		{
			if (r.getTop() < mBoundTop ||
				r.getBottom() > mBoundBottom ||
				r.getLeft() < mBoundLeft ||
				r.getRight() > mBoundRight)
				return true;
			else
				return false;
		}
 
		RectList::iterator getListBegin(){return mList.begin();}
		RectList::iterator getListEnd(){return mList.end();}
 
		void clear(){mList.clear();}
 
		RectList::iterator addData(Rect &Data)
		{
			if (isOutOfBounds(Data))
				return mList.end(); // out of bounds, error
 
			switch (mMethod)
			{
			case PLACE_ABOVE:
				return addDataAbove(Data);
			case PLACE_BELOW:
				return addDataBelow(Data);
			default:
				return mList.end(); // incorrect method type, error
			};
		}
 
	protected:
		// List of orderedd rectangles
		// All items in list are assumed ordered and within established Bounds
		RectList mList;
 
		// The overlapping rectangles are placed at a mMinDistance from the other ones
		unsigned short mMinDistance;
 
		// This gets calculated "on the fly"
		unsigned short mMaxRectHeight;
 
		// An overlapping rectangle is moved on Y axis (Above or Below) untill
		//  a place is found where it doesn't overlap any other rectangle.
		// mDepth is the number of times an overlapping rectangle will be moved
		//  in order to find a non-overlapping place.
		//
		// (mDepth = 0) - the search will go on untill a place is found.
		// (mDepth > 0) - the search will go on <mDepth> times
		unsigned short mDepth;	
 
		// Don't use these directly, use addData instead
		RectList::iterator addDataAbove(Rect &Data);
		RectList::iterator addDataBelow(Rect &Data);
 
		// Const variables can only be set in the constructor and certify the RectList integrity.
 
		// Method Type
		const MethodType mMethod;
 
		// Bounds
		const unsigned short mBoundTop;
		const unsigned short mBoundLeft;
		const unsigned short mBoundBottom;
		const unsigned short mBoundRight;
	};
	
	typedef RefPtr<MovableTextOverlay>				MovableTextOverlayPtr;
	typedef RefPtr<MovableTextOverlayAttributes>	MovableTextOverlayAttribsPtr;
	typedef MasterPtr<MovableTextOverlay>			MovableTextOverlayMasterPtr;
};

#endif